package br.org.bertol.mestrado.engine;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import org.apache.log4j.Logger;

import br.org.bertol.mestrado.engine.optimisation.entity.Path;
import br.org.bertol.mestrado.engine.optimisation.hillclimbing.HillClimbing;
import br.org.bertol.mestrado.engine.optimisation.hillclimbing.Operador;
import br.org.bertol.mestrado.engine.search.Greedy;

/**
 * @author contaqualquer
 */
public class EngineHillClimbing extends AbstractSearchEngine<Integer> {

    /***/
    private final transient Operador                          operador;
    /***/
    private final transient List<HillClimbing<Integer, Path>> betterCustomPaths;
    /***/
    private final transient List<Path>                        customPaths;
    /***/
    private final transient List<HillClimbing<Integer, Path>> betterRandomPaths;
    /***/
    private final transient List<Path>                        randomPaths;
    /***/
    private final transient boolean                           bestFirst;
    /***/
    private final transient Greedy<Integer>                   greedy;

    public EngineHillClimbing(final CountDownLatch startS,
        final CountDownLatch stopS, final int numEval,
        final Objective[] objectives, final String storeP,
        final String sID, final Operador operador,
        final Greedy<Integer> greedy,
        final boolean isBF) {

        super(startS, stopS, numEval, storeP, sID, objectives);

        setName(getName() + " | Operador " + operador.name() + " | Best First "
                + isBF);

        this.operador = operador;

        betterCustomPaths = new ArrayList<HillClimbing<Integer, Path>>();

        betterRandomPaths = new ArrayList<HillClimbing<Integer, Path>>();

        customPaths = new ArrayList<Path>();

        randomPaths = new ArrayList<Path>();

        bestFirst = isBF;

        this.greedy = greedy;

        this.greedy.setVerify(super.verify);
    }

    public final void run() {
        try {
            // deixa sistema ponto para entrar em a��o
            startSystem.await();

            if (verify.isSystemEnable()) {
                // aramazena resultado do greedy em arquivo
                final PrintWriter outFile = new PrintWriter(new BufferedWriter(
                        new FileWriter(storePath + "/" + systemID
                        + "/resultado_avaliacao_" + systemID + "_"
                        + verify.getObjectives().toString() + "_"
                        + operador.name() + "_"
                        + (bestFirst ? "BF" : "BN") + ".txt", true)));

                Logger.getLogger(this.getClass()).info("Start");

                customPaths.addAll(greedy.greedySearch(verify.getClasses()));

                final int num_hill_climbing_threads = customPaths.size() > 5 ? 5
                        : customPaths.size();

                randomPaths
                        .addAll(randomSolution
                                .generateRandomHillCimbingSolutions(num_hill_climbing_threads));

                outFile
                        .println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");

                outFile.println(getName());

                doneHeuristic = new CountDownLatch(num_hill_climbing_threads
                        + (randomPaths.size()));

                outFile.println("Sistema " + systemID + " Gera��o Greedy ");

                for (int i = 0; i < num_hill_climbing_threads; i++) {
                    final Path path = customPaths.get(i);

                    outFile.println("[Caminho: " + path.toString() + "]");

                    final HillClimbing<Integer, Path> hillClimbing = new HillClimbing<Integer, Path>(
                            path, numIteracoes, bestFirst, operador,
                            startHeuristic, doneHeuristic,
                            (ArrayList<Integer>) verify.getClasses(),
                            verify.getObjectives());

                    hillClimbing.start();

                    betterCustomPaths.add(hillClimbing);
                }

                outFile.println("Sistema " + systemID + " Gera��o Aleat�ria ");

                for (int i = 0; i < num_hill_climbing_threads; i++) {
                    final Path path = randomPaths.get(i);

                    outFile.println("[Caminho: " + path.toString() + "]");

                    final HillClimbing<Integer, Path> hillClimbing = new HillClimbing<Integer, Path>(
                            path, numIteracoes, bestFirst, operador,
                            startHeuristic, doneHeuristic,
                            (ArrayList<Integer>) verify.getClasses(),
                            verify.getObjectives());

                    hillClimbing.start();

                    betterRandomPaths.add(hillClimbing);
                }

                startHeuristic.countDown();

                doneHeuristic.await();

                outFile
                        .println("Sistema "
                                + systemID
                                + " Hill Climbing - Resultado de avalia��o de solu��es customizadas.");

                for (int i = 0; i < num_hill_climbing_threads; i++) {
                    outFile.println("[Numero de avalia��es realizadas: "
                            + betterCustomPaths.get(i).getNumeroAvaliacoes()
                            + " | Numero de restarts realizados: "
                            + betterCustomPaths.get(i).getNumRestart()
                            + " | Numero de melhoras realizados: "
                            + betterCustomPaths.get(i).getNumeroMelhoras()
                            +
                                                // " | Ganho obtido: "+(customPaths.get(i).getHeigtht()
                            // -
                            // betterCustomPaths.get(i).getBestPath().getHeigtht())+
                            " | Caminho: "
                            + betterCustomPaths.get(i).getBestPath().toString()
                            + "]");
                }

                outFile
                        .println("Sistema "
                                + systemID
                                + " Hill Climbing - Resultado de avalia��o de solu��es rand�mica.");

                for (int i = 0; i < num_hill_climbing_threads; i++) {
                    outFile.println("[Numero de avalia��es realizadas: "
                            + betterRandomPaths.get(i).getNumeroAvaliacoes()
                            + " | Numero de restarts realizados: "
                            + betterRandomPaths.get(i).getNumRestart()
                            + " | Numero de melhoras realizadas: "
                            + betterRandomPaths.get(i).getNumeroMelhoras()
                            +
                                                // " | Ganho obtido: "+(randomPaths.get(i).getHeigtht()
                            // -
                            // betterRandomPaths.get(i).getBestPath().getHeigtht())+
                            " | Caminho: "
                            + betterRandomPaths.get(i).getBestPath().toString()
                            + "]");
                }

                outFile
                        .println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");

                outFile.close();

                Logger.getLogger(this.getClass()).info("End");
            }
        } catch (InterruptedException e) {
            Logger.getLogger(this.getClass()).error(
                                                    "Erro ao executar sistema "
                    + systemID, e);
        } catch (Exception e) {
            Logger.getLogger(this.getClass()).error(
                                                    "Erro ao executar sistema "
                    + systemID, e);
        }
        // notifica main thread
        stopSystem.countDown();
    }

    /**
     * @return the betterCustomPaths
     */
    public final List<HillClimbing<Integer, Path>> getBetterCustomPaths() {
        return betterCustomPaths;
    }

    /**
     * @return the betterRandomPaths
     */
    public final List<HillClimbing<Integer, Path>> getBetterRandomPaths() {
        return betterRandomPaths;
    }

    /**
     * @return the operador
     */
    public final Operador getOperador() {
        return operador;
    }

    /**
     * @return the customPaths
     */
    public final List<Path> getCustomPaths() {
        return customPaths;
    }

    /**
     * @return the randomPaths
     */
    public final List<Path> getRandomPaths() {
        return randomPaths;
    }
}
